package com.nulldev.util.napi;

import java.util.HashMap;
import java.util.LinkedList;

import com.nulldev.util.java.JVMUtil;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;

public class nullAPI {

	private final Logger log;
	private final LinkedList<APICommand> commands = new LinkedList<APICommand>();
	private final HashMap<String, Object> varStore = new HashMap<String, Object>();
	
	public nullAPI() {
		this.log = LoggerFactory.getLogger(true, true);
	}
	
	public Object putVariable(final String name, final Object item) {
		return this.varStore.put(name, item);
	}
	
	public Object getVariable(final String name) {
		return this.varStore.get(name);
	}
	
	public boolean registerCommand(final APICommand command) {
		return this.commands.add(command);
	}
	
	public boolean isCommand(final String commandName) {
		for (final APICommand c : cloneList()) {
			if (c.getName().equals(commandName)) {
				return true;
			}
		}
		return false;
	}
	
	public void println() {
		for (final APICommand c : cloneList()) {
			synchronized (System.err) {
				synchronized (System.out) {
					JVMUtil.async_println(c.getName());
				}
			}
		}
	}
	
	public LinkedList<APICommand> cloneList() {
		return (LinkedList<APICommand>) this.commands.clone();
	}
	
	public Class[] getArgs(final String commandName) {
		for (final APICommand c : cloneList()) {
			if (c.getName().equals(commandName)) {
				return c.getArguments();
			}
		}
		return APICall.NO_ARGS;
	}
	
	public Object invoke(final String commandName, final Object... args) {
		for (final APICommand c : cloneList()) {
			if (c.getName().equals(commandName)) {
				final int exc = c.getExpectedArgs();
				if (exc == -1) {
					return c.call(args);
				}else {
					if (args.length != exc) {
						throw new UnsupportedOperationException("nullAPI.invoke(" + commandName + ") | Got " + args.length + " args, expected " + exc + " args.");
					}
				}
			}
		}
		log.error("invoke(" + commandName + ") -> Unable to find call!");
		return APICall.VOID;
	}
	
	public Object invoke(final String commandName) {
		for (final APICommand c : cloneList()) {
			if (c.getName().equals(commandName)) {
				return c.call();
			}
		}
		return APICall.VOID;
	}

	public boolean registerAll(final LinkedList<APICommand> commands) {
		return this.commands.addAll(commands);
	}

	public void merge(final nullAPI api) {
		for (final APICommand command : api.cloneList()) {
			this.registerCommand(command);
		}
	}
}
